'use client'; import { useRouter } from 'next/navigation'; import { useActionState, useState } from 'react'; import { FoxButton } from '@fox/core/components/fox-button'; import { FoxButtonGroup } from '@fox/core/components/fox-button-group'; import { FoxButtonPanel } from '@fox/core/components/fox-button-panel'; import { FoxCard } from '@fox/core/components/fox-card'; import { FoxDescriptionList } from '@fox/core/components/fox-description-list'; import { FoxHelperText } from '@fox/core/components/fox-helper-text'; import { FoxInput } from '@fox/core/components/fox-input'; import { FoxPageHeader } from '@fox/core/components/fox-page-header'; import { FoxSelect } from '@fox/core/components/fox-select'; import { submitFormAction } from '@/app/_hooks/submit-form-action'; import { useBlockingAction } from '@/app/_hooks/use-blocking-action'; import { useFeedback } from '@/app/_hooks/use-feedback'; import type { CommonCode } from '@/lib/domain/common-code'; import { MERCHANT_DETAIL_VISIBILITY_OPTIONS, MERCHANT_VOUCHER_USE_OPTIONS, formatMerchantDetailStatusLabel, formatOptionalValue, type MerchantDetail, } from '@/lib/domain/merchant-detail'; import { CLOSED_DAYS_MAX_LENGTH, DESCRIPTION_MAX_LENGTH, INITIAL_MERCHANT_DETAIL_FORM_STATE, SORT_ORDER_MAX, } from '@/lib/domain/merchant-detail-form'; import { MERCHANT_DETAILS_PATH } from '@/lib/domain/merchant-detail-query'; import type { MerchantProduct } from '@/lib/domain/merchant-product'; import { formatPhoneNumber } from '@/lib/domain/phone-number'; import { deleteMerchantDetailAction, saveMerchantDetailAction, updateMerchantDetailVisibilityAction, } from '../../_actions'; import { MerchantChoiceChips } from '../../_components/merchant-choice-chips'; import { MerchantImageField } from '../../_components/merchant-image-field'; import { MerchantProductTable } from '../../_components/merchant-product-table'; import styles from './merchant-detail-form.module.scss'; interface MerchantDetailFormProps { detail: MerchantDetail; products: MerchantProduct[]; categories: CommonCode[]; } /** * 가맹몰 상세 등록·수정 — 기획 BO-MAL-002(등록)·003(수정)을 한 화면으로 합쳤다. 상세는 * 가맹점당 하나라 `frcsNo`만으로 등록인지 수정인지 갈린다(`detail.hasDetail`). * * 업체명·연락처·우편번호·주소는 가맹점 원본이라 읽기 전용이다(사용자 확정). 기획의 [주소 검색]은 * 가맹점 관리 화면의 것이고 여기서는 값을 바꾸지 않는다. */ export function MerchantDetailForm({ detail, products, categories }: MerchantDetailFormProps) { const router = useRouter(); const { showAlert, hideAlert, showToast } = useFeedback(); const [isBusy, startAction] = useBlockingAction(); const [state, formAction, isPending] = useActionState( saveMerchantDetailAction, INITIAL_MERCHANT_DETAIL_FORM_STATE ); const isEdit = detail.hasDetail; const errors = state.status === 'error' ? (state.errors ?? {}) : {}; const [categoryCode, setCategoryCode] = useState(detail.categoryCode ?? ''); const [visibility, setVisibility] = useState([ detail.isVisible === false ? 'N' : 'Y', ]); const [voucherUse, setVoucherUse] = useState(['Y']); function toggleVisibility() { const next = detail.isVisible === false; startAction(async () => { const result = await updateMerchantDetailVisibilityAction(detail.id, next); showToast( result.status === 'error' ? { variant: 'danger', message: result.message } : { variant: 'success', message: next ? '노출로 바꿨습니다.' : '비활성으로 바꿨습니다.' } ); }); } function runDelete() { hideAlert(); startAction(async () => { const result = await deleteMerchantDetailAction(detail.id); if (result.status === 'error') { showToast({ variant: 'danger', message: result.message }); return; } showToast({ variant: 'success', message: '삭제했습니다.' }); router.push(MERCHANT_DETAILS_PATH); }); } function confirmDelete() { showAlert({ variant: 'danger', title: '가맹몰 상세를 삭제하시겠습니까?', message: `"${detail.name}"의 상세 정보를 삭제합니다. 삭제 후에는 되돌릴 수 없습니다.`, actions: ( ), }); } return ( <>
submitFormAction(event, formAction)} className={styles.form} >
({ value: code.code, label: code.label }))} value={categoryCode} onValueChange={setCategoryCode} error={Boolean(errors.categoryCode)} /> {errors.categoryCode && }
{isEdit && ( Boolean(value)) .join(' / ') || '-', }, { key: 'status', term: '현재 상태', description: formatMerchantDetailStatusLabel(detail) }, ]} /> )} {state.status === 'error' && state.message && ( )} {isEdit && ( <> )} router.push(MERCHANT_DETAILS_PATH)} /> ); }